#!/usr/bin/env python
# -*- coding:utf-8 -*-

# 古腾堡语料库
# 古腾堡语料库的文件标识符
"""
import nltk
print(nltk.corpus.gutenberg.fileids())
# 挑选这些文本的第一个, 并赋予它简短名称emma,找出它包含多少个词.
# emma = nltk.corpus.gutenberg.words('austen-emma.txt')
# print(len(emma))

emm = nltk.Text(nltk.corpus.gutenberg.words('austen-emma.txt'))
print(emm.concordance("surprize"))
'''
定义emma时,调用了NLTK中的corpus包中的gutenberg对象的words()函数.
'''


from nltk.corpus import gutenberg
# print(gutenberg.fileids())
'''
通过循环遍历前面列出的 gutenberg 文件标识符链表相应
的 fileid，然后计算统计每个文本。为了使输出看起来紧凑，我们使用函数int()来确保数
字都是整数。
'''
for fileid in gutenberg.fileids():
    num_chars = len(gutenberg.raw(fileid))  
    # raw()函数给我们没有进行过任何语言学处理的文件的内容。
    num_words = len(gutenberg.words(fileid))
    num_sents = len(gutenberg.sents(fileid))
    num_vocab = len(set([w.lower() for w in gutenberg.words(fileid)]))
    print(int(num_chars/num_words),int(num_words/num_sents),int(num_words/num_vocab),fileid)
'''
这个程序显示每个文本的三个统计量：平均词长、平均句子长度和本文中每个词出现的
平均次数. 平均词长是3，num_chars变量将空白字符也统计上了,所以是4个.
sents()函数把文本划分成句子,每一个句子是一个词链表.
'''
"""

"""
from nltk.corpus import gutenberg

macbeth_sentences = gutenberg.sents('shakespeare-macbeth.txt')
print(macbeth_sentences)
print(macbeth_sentences[1037])
print('=='*20)
longest_len = max([len(s) for s in macbeth_sentences])
print([s for s in macbeth_sentences if len(s) == longest_len])
"""



## 网络和聊天文本
"""
# NLTK的网络文本小集合的内容包括Firefox交流论坛,个人广告和葡萄酒的评论
from nltk.corpus import webtext
for fileid in webtext.fileids():
    print(fileid, webtext.raw(fileid)[:65],'...')


# 即时消息聊天会话语料库
from nltk.corpus import nps_chat
chatroom = nps_chat.posts('10-19-20s_706posts.xml')
print(chatroom[123])
"""


# 布朗语料库
## 布朗语料库是第一个百万词级的英语电子语料库,这个语料库包含500个不同来源的文本. 
# 可以将语料库作为词链表或者句子链表来访问
# 指定特定的类别或文件阅读
"""
from nltk.corpus import brown
print(brown.categories())
print('--'*20)
print(brown.words(categories='news'))
print('--'*10)
print(brown.words(fileids=['cg22']))
print(brown.sents(categories=['news','editorial','reviews']))
'''
布朗语料库是一个研究文体之间的系统性差异-- 叫做文体学的语言学研究.
'''
"""


# 比较不同文体中的情态动词的用法
# 第一步: 产生特定文体的计数
"""
import nltk
from nltk.corpus import brown
news_text = brown.words(categories='news')
fdist = nltk.FreqDist([w.lower() for w in news_text])
modals = ['can','could','may','might','must','will']
for m in modals:
    print(m + ':',fdist[m])

# 第二步: 统计每一个感兴趣的文体, 使用NLTK提供的带条件的频率分布函数.
cfd = nltk.ConditionalFreqDist(
    (genre,word)
    for genre in brown.categories()
    for word in brown.words(categories=genre)
)
genres = ['news','religion','hobbies','science_fiction','romance','humor']
modals = ['can','could','may','might','must','will']
cfd.tabulate(conditions=genres,samples=modals)
# 新闻文体中最常见的情态动词是will,而言情文体中最常见的情态动词是could。
"""


# 路透社语料库
"""
from nltk.corpus import reuters
print(reuters.fileids())
print(reuters.categories())
"""
# 路透社语料库类别是有互相重叠的,因为新闻报道涉及多个主题.
# 查找由一个或多个文档涵盖的主题,也可以查找包含在一个或多个类别中的文档.
# 语料库方法既接受单个的fileid也接受fileids列表作为参数.
"""
print(reuters.categories('training/9865'))
print(reuters.categories(['training/9865','training/9880']))
print('=='*20)
print(reuters.fileids('barley'))
print(reuters.fileids(['barley','corn']))

# 以文档或类别为单位查找我们想要的词或句子. 最开始的几个词是标题,以大写字母存储.
print(reuters.words('training/9865')[:14])
print(reuters.words(['training/9865','training/9880']))
print('=='*30)
print(reuters.words(categories='barley'))
print(reuters.words(categories=['barley','corn']))
"""


# 就职演说语料库
"""
from nltk.corpus import inaugural
print(inaugural.fileids())
print([fileid[:4] for fileid in inaugural.fileids()])
"""
# 每个文本的年代都出现在它的文件名中。 要从文件中获得年代,使用fileid[:4]提取前四个字符.
# 用lower()将词汇转换成小写, 用startswith()检查是否以目标词汇america或citizen开始.
"""
import nltk
from nltk.corpus import inaugural

cfd = nltk.ConditionalFreqDist(
    (target,file[:4])
    for fileid in inaugural.fileids()
    for w in inaugural.words(fileid)
    for target in ['america','citizen']
    if w.lower().startswith(target))
print(cfd.plot())
执行时报错:NameError: name 'file' is not defined
未查明原因.
"""

# 其他语言的语料库
"""
# udhr 是世界人权宣言
# 用条件频率分布来研究世界人权宣言(udhr)语料库中不同语言版本中的字长差异.
import nltk
from nltk.corpus import udhr
languages = ['Chickasaw','English','German_Deutsch','Greenlandic_InuktiKut','Hungarian_Magyar','Ibibio_Efik']
cfd = nltk.ConditionalFreqDist(
    (lang,len(word))
    for lang in languages
    for word in udhr.words(lang + '-Latin1')
)
print(cfd.plot(cumulative=True))
"""


# 载入你自己的语料库
"""
# Linux
from nltk.corpus import PlaintextCorpusReader
corpus_root = '/usr/share/dict'
wordlists = PlaintextCorpusReader(corpus_root,'.*')
wordlists.fileids()
print(wordlists.words('con'))

# Windows
# 使用BracketParseCorpusReader,指定corpus_root为存放语料库中解析,并指定file_pattern与它的字文件夹中包含的文件匹配.
from nltk.corpus import BracketParseCorpusReader
corpus_root = r"C:\corpora\txt"
file_pattern = r".*/wsg"
ptb = BracketParseCorpusReader(corpus_root,file_pattern)
ptb.fileids()
"""



# 按文体计数词汇
"""
# 以布朗语料库的每一部分为条件的条件频率分布,并按照每个条件计数词汇。FreqDist()以一个简单的链表作为输入,ConditionalFreqDist()以一个配对链表作为输入.
import nltk
from nltk.corpus import brown
cfd = nltk.ConditionalFreqDist(
    (genre,word)
    for genre in brown.categories()
    for word in brown.words(categories=genre))    
# 只看两个文体: 新闻和言情,对于每个文体,遍历文体中的每个词意产生文体与词的配对.
genre_word = [(genre,word)
            for genre in ['news','romance']
            for word in brown.words(categories=genre)]
print(len(genre_word))
# 链表genre_word的前几个配对将是('news',word)形式,最后几个配对是('romance',word)形式.
print(genre_word[:4])
print(genre_word[-4:])
print('=='*20)
# 使用此配对链表创建一个ConditionalFreqDist,并将它保存在一个变量cfd中.
cfd = nltk.ConditionalFreqDist(genre_word)
print(cfd)
print(cfd.conditions())
print('----'*20)
# 访问这两个条件,每一个都只是一个频率分布
print(cfd['news'])
print(cfd['romance'])
# print('==='*5)
# print(list(cfd['romance']))
# print(cfd['romance']['could'])
"""



# 绘制分布图和分布表
"""
from nltk.corpus import inaugural
import nltk
cfd = nltk.ConditionalFreqDist(
    (target,fileid[:4])
    for fileid in inaugural.fileids()
    for w in inaugural.words(fileid)
    for target in ['america','citizen']
    if w.lower().startswith(target)
)
"""
"""
import nltk
from nltk.corpus import udhr
languages = ['Chickasaw','English','German_Deutsch',
    'Greenlandic_Inuktikut','Hungarian_Magyar','Ibibio_Efik']
cfd = nltk.ConditionalFreqDist(
    (lang,len(word))
    for lang in languages
    for word in udhr.words(lang + '-Latin1'))

# 为两种语言和长度少于10个字符的词汇绘制累计频率数据表,解释一下上排最后一个单元格中数值的含义是英文文本中9个或少于9个字符长的词有1638个.
print(cfd.tabulate(conditions=['English','German_Deutsch'],samples=range(10),cumulative=True))
"""



# 使用双连词生成随机文本
"""
import nltk
# 使用条件频率分布创建一个双连词表.bigrams()函数接受一个词汇链表,并建立一个连续的词对链表.
sent = ['In','the','beginning','God','created','the','heaven','and','the','earth','.']
print(nltk.bigrams(sent))

# 函数generate_model()包含一个简单的循环来生成文本.调用这个函数时,选择一个词作为初始内容.
# 进入循环后,输入变量word的当前值,重新设置word为上下文中最可能的标识符.
# 从可用的词汇中随机选择下一个词.
import nltk
from nltk import corpus
# 产生随机文本: 获得文本中所有的双连词,然后构造一个条件频率分布来记录哪些词汇最有可能跟在给定词的后面.
def generate_model(cfdist,word,num=15):
    for i in range(num):
        print(word)
        word = cfdist[word].max()

text = nltk.corpus.genesis.words('english-kjv.txt')
bigrams = nltk.bigrams(text)
cfd = nltk.ConditionalFreqDist(bigrams)
print(cfd['living'])
print(generate_model(cfd,'living'))
"""



# 函数: 代码重用
"""
def plural(word):
    if word.endswith('y'):
    # endswith()函数是方法.
        return word[:-1] + 'ies'
    elif word[-1] in 'sx' or word[-2:] in ['sh','ch']:
        return word + 'es'
    elif word.endswith('an'):
        return word[:-2] + 'en'
    else:
        return word + 's'

print(plural('fairy'))
print(plural('woman'))
"""




# 词典资源
## 词汇列表语料库
### 过滤文本: 此程序计算文本的词汇表,然后删除所有在现有的词汇列表中出现的元素
"""
import nltk
from nltk.corpus import gutenberg
def unusual_words(text):
    text_vocab = set(w.lower() for w in text if w.isalpha())
    english_vocab = set(w.lower() for w in nltk.corpus.words.words())
    unusual = text_vocab.difference(english_vocab)
    return sorted(unusual)

print(unusual_words(nltk.corpus.gutenberg.words('austen-sense.txt')))
print('=='*20)
print(unusual_words(nltk.corpus.nps_chat.words()))
"""


## 停用词语料库,就是那些高频词汇. 从文本中过滤掉.
"""
import nltk
from nltk.corpus import stopwords
print(stopwords.words('english'))
# 定义一个函数来计算文本中没有在停用词列表中的词的比例
def content_fraction(text):
    stopwords = nltk.corpus.stopwords.words('english')
    content = [w for w in text if w.lower() not in stopwords]
    return len(content) / len(text)

print(content_fraction(nltk.corpus.reuters.words()))
"""


## 使用词典资源来过滤文本语料的内容
"""
'''
程序遍历每一个词,对于每一个词检查是否符合条件。检查必须出现的字母和长度限制很容易.
'''
import nltk
puzzle_letters = nltk.FreqDist('egivrvonl') 
obligatory = 'r'
wordlist = nltk.corpus.words.words()
print([w for w in wordlist if len(w) >= 6  # 长度限制. 
                and obligatory in w  # 字母
                # FreqDist比较法允许我们检查每个字母在候选词中的频率是否小于或等于相应的字母在拼词谜题中的频率.
                and nltk.FreqDist(w) <= puzzle_letters])  
"""


## 另一个词汇列表是名字语料库,包括8000个按性别分类的名字,男性和女性的名字存储在单独的文件中,找出同时出现在两个文件中的名字.
"""
import nltk
names = nltk.corpus.names
print(names.fileids())
male_names = names.words('male.txt')
female_names = names.words('female.txt')
print([w for w in male_names if w in female_names])

cfd = nltk.ConditionalFreqDist(
    (fileid,name[-1])
    for fileid in names.fileids()
    for name in names.words(fileid))

print(cfd.plot())
'''
会输出一个图: 条件频率分布.
此图显示男性和女性名字结尾字母. 
'''
"""



## 发音的词典
### 词典资源是表格,NLTK中包括美国英语的CMU发音词典.
"""
import nltk
entries = nltk.corpus.cmudict.entries() 
# print(len(entries))
# print([entry for entry in entries[39943:39951]])
# 对每一个词,这个词典资源提供语音的代码-不同的声音不同的标签叫做音素.
# 每个条目由两部分组成,每次通过循环时,word被分配条目的第一个部分,pron被分配条目的第二部分.

for word,pron in entries:
    if len(pron) == 3: # 程序扫描词典中发音包含三个音素的条目.
        ph1,ph2,ph3 = pron  # 条件为真,将pron内容分配给三个新的变量.
        if ph1 == 'P' and ph3 == 'T':
            print(word,ph2)

# 使用链表推导,这段程序找到所有发音结尾与nicks相似的词汇.使用此方法来找到押韵的词.
syllable = ['N','IH0','K','S']
# print([word for word,pron in entries if pron[-4:] == syllable])
print([w for w,pron in entries if pron[-1] == 'M' and w[-1] == 'n'])
print('--'*20)
print(sorted(set(w[:2] for w,pron in entries if pron[0] == 'N' and w[0]!= 'n')))
# 音素包含数字表示主重音,词重音和无重音.
"""


# 定义一个函数来提取重音数字,然后扫描词典,找到具有特定重音模式的词汇.
"""
import nltk
entries = nltk.corpus.cmudict.entries()

def stress(pron):
    return [char for phone in pron for char in phone if char.isdigit()]
'''
自定义函数stress()调用一个内含条件的链表推理,还有一个双层嵌套循环.
'''
print([w for w,pron in entries if stress(pron) == ['0','1','0','2','0']])
print('=='*20)
print([w for w,pron in entries if stress(pron) == ['0','2','0','1','0']])
"""




# 使用条件频率分布来帮助找到词汇的最小受限集合. 
# 找到所有p开头的三音素词,并按照它们的第一个和最后一个音素来分组.
"""
import nltk
entries = nltk.corpus.cmudict.entries()

p3 = [(pron[0]+'-'+pron[2],word)
    for (word,pron) in entries
    if pron[0] == 'P' and len(pron) == 3]

cfd = nltk.ConditionalFreqDist(p3)
for template in cfd.conditions():
    if len(cfd[template]) > 10:
        words = cfd[template].keys()
        wordlist = ' '.join(words)
        print(template,wordlist[:70] + "...")
"""


# 通过查找特定词汇来访问,不必遍历整个词典. 通过指定词典的名字后面跟一个包含在方括号里的关键字来查词典.
"""
import nltk
prondict = nltk.corpus.cmudict.dict()
print(prondict['fire']) # 这个关键字有值.
prondict['blog'] = [['B','L','AA1','G']]  # 这个关键字没有值,所以要为关键字分配一个值.
print(prondict['blog'])
"""


# 用任何词典资源来处理文本.
# 下面的文本到发音函数在发音词典中查找文本中每个词.
"""
import nltk
prondict = nltk.corpus.cmudict.dict()
text = ['natural','language','processing']
print([ph for w in text for ph in prondict[w][0]])
"""


# 比较词表
"""
'''
NLTK中包含了斯瓦迪士核心词列表,几种语言中阅200个常用词的列表,语言标识符使用ISO639双字母码.
'''
from nltk.corpus import swadesh
# print(swadesh.fileids())
# print(swadesh.words('en'))
# 通过在entries()方法中指定一个语言链表来访问来访问多语言中的同源词.
# fr2en = swadesh.entries(['fr','en'])
# print(fr2en)
#translate = dict(fr2en)
#print(translate['chien'])
#print(translate['jeter'])
# 使用dict()函数把德语-英语和西班牙语-英语对相互转换成一个词典,然后用这些添加的映射更新原来的翻译词典.
#de2en = swadesh.entries(['de','en']) # 德语
#es2en = swadesh.entries(['es','en']) # 西班牙语
#print(translate.update(dict(de2en)))
#print(translate.update(dict(es2en)))
#print('=='*20)
#print(translate['Hund'])
#print(translate['perro'])
# 比较日尔曼语族和拉丁语族的不同
languages = ['en','de','nl','es','fr','pt','la']
for i in [139,140,141,142]:
    print(swadesh.entries(languages))
"""    


# 词汇工具: Toolbox和Shoebox
# 下面是一个罗托卡特语的词典.
"""
from nltk.corpus import toolbox
print(toolbox.entries('rotokas.dic'))
# 条目包括一系列的属性-值对.
"""

# WordNet
## 意义与同义词
"""
from nltk.corpus import wordnet as wn 
print(wn.synsets('motorcar'))
# 输出为car.n.01被称为synset或"同义词集",意义相同的词或词条的集合.
print(wn.synset('car.n.01').lemma_names)
# 同义词集中的每个词可以有多种含义.
print(wn.synset('car.n.01').definition)
print(wn.synset('car.n.01').examples)
# 同义词集和词的配对叫做词条. 可以得到指定同义词集的所有词条,查找特定的词条.得到一个词条对应的同义词集,也可以得到一个词条的名字.
print(wn.synset('car.n.01').lemmas)
print(wn.lemma('car.n.01.automobile'))
print('--'*20)
print(wn.lemma('car.n.01.automobile').synset)
print(wn.lemma('car.n.01.automobile').name)
# 与词automobile和motorcar这些意义明确的只有一个同义词集的词不同,词car是含糊的,有五个同义词集.
print(wn.synsets('car'))
print('=='*20)
for synset in wn.synsets('car'):
    print(synset.lemma_names)

# 用下面的方式访问所有包含词car的词条.
print(wn.lemmas('car'))
"""

# 下位词
"""
from nltk.corpus import wordnet as wn 
# motorcar = wn.synset('car.n.01')
# types_of_motorcar = motorcar.hyponyms()
# print(types_of_motorcar[26])
motorcar = wn.synset('car.n.01')
print(motorcar.hypernyms())
paths = motorcar.hypernym_paths()
print(len(paths))
print('--'*20)
print([synset.name for synset in paths[0]])
print('=='*20)
print([synset.name for synset in paths[1]])
# 得到一个最一般的三位(或根上位)同义词集:
print(motorcar.root_hypernyms())
"""


# 更多的词汇关系
# 一棵树的部分是树干和树冠是part_meronyms(),一棵树的实质是包括心材和边材组成的即substance_meronyms().
# 树木的集合形成了一个森林:member_holonyms()
"""
from nltk.corpus import wordnet as wn 
print(wn.synset('tree.n.01').part_meronyms())
print(wn.synset('tree.n.01').substance_meronyms())
print(wn.synset('tree.n.01').member_holonyms())
for synset in wn.synsets('mint',wn.NOUN):
    print(synset.name,synset.definition)
print(wn.synset('mint.n.04').part_holonyms())
print(wn.synset('mint.n.04').substance_holonyms())
# 动词也有关系
print('=='*20)
print(wn.synset('walk.v.01').entailments())
print(wn.synset('eat.v.01').entailments())
print(wn.synset('tease.v.03').entailments())
# 词条之间的一些词汇关系
print('==='*30)
print(wn.lemma('supply.n.02.supply').antonyms())
print(wn.lemma('rush.v.01.rush').antonyms())
print(wn.lemma('horizontal.a.01.horizontal').antonyms())
print(wn.lemma('staccato.r.01.staccato').antonyms())
"""


# 语义相似度
from nltk.corpus import wordnet as wn 
right = wn.synset('right_whale.n.01')
orca = wn.synset('orca.n.01')
minke = wn.synset('minke_whale.n.01')
tortoise = wn.synset('tortoise.n.01')
novel = wn.synset('novel.n.01')
print(right.lowest_common_hypernyms(minke))
print(right.lowest_common_hypernyms(orca))
print(right.lowest_common_hypernyms(tortoise))
print(right.lowest_common_hypernyms(novel))
# 通过查找每个同义词集深度量化这个一般的概念:
print(wn.synset('baleen_whale.n.01').min_depth())
print(wn.synset('whale.n.02').min_depth())
print(wn.synset('vertebrate.n.01').min_depth())
print(wn.synset('entity.n.01').min_depth())
print('=='*20)
print(right.path_similarity(minke))
print(right.path_similarity(orca))
print(right.path_similarity(tortoise))
print(right.path_similarity(novel))

